﻿// Copyright (C) 2009 Monadsphere <monadshpere@gmail.com>
//
// This file is part of Monadsphere.
//
// Monadsphere is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Monadsphere is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Monadsphere. If not, see <http://www.gnu.org/licenses/>.

using monadsphere.js;
using monadsphere.browser;
using monadsphere.dom.events;

/// <summary>
/// This namespace implements all DOM Level 3 Core Specification.
/// See http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/ for details.
/// </summary>
/// 

namespace monadsphere.dom {

    public class DOMStringList: JSVar {
        public DOMStringList(JSVar src)
            : base(src) {
        }

        public JSNumber length {
            get {
                return getProperty<JSNumber>("length");
            }
        }
        public JSString item(JSNumber index) {
            return callFunction<JSString>("index");
        }

        public JSString this[JSNumber index] {
            get {
                return getIndex<JSString>(index);
            }
        }
        public JSBoolean contains(JSString str) {
            return callFunction<JSBoolean>("str");
        }
    }

    public class NameList: JSVar {
        protected NameList() {
        }

        public NameList(JSVar src)
            : base(src) {
        }

        JSNumber length {
            get {
                return getProperty<JSNumber>("length");
            }
        }

        JSString getName(JSNumber index) {
            return callFunction<JSString>("getName", index);
        }

        JSString getNamespaceURI(JSNumber index) {
            return callFunction<JSString>("getNamespaceURI", index);
        }

        public JSBoolean contains(JSString str) {
            return callFunction<JSBoolean>("contains", str);
        }

        public JSBoolean containsNS(JSString namespaceURI, JSString name) {
            return callFunction<JSBoolean>("containsNS", namespaceURI, name);
        }
    }

    public class DOMImplementationList: JSVar {
        JSNumber length {
            get {
                return getProperty<JSNumber>("length");
            }
        }

        public DOMImplementation item(JSNumber index) {
            return callFunction<DOMImplementation>("item", index);
        }

        public DOMImplementation this[JSNumber index] {
            get {
                return getIndex<DOMImplementation>(index);
            }
        }
    }

    public class DOMImplementationSource: JSVar {
        public DOMImplementation getDOMImplementation(JSString features) {
            return callFunction<DOMImplementation>("getDOMImplementation", features);
        }

        public DOMImplementationList getDOMImplementationList(JSString features) {
            return callFunction<DOMImplementationList>("getDOMImplementationList", features);
        }
    }

    public class DOMImplementation: JSVar {
        public JSBoolean hasFeature(JSString feature,
                                  JSString version) {
            return callFunction<JSBoolean>("hasFeature", feature, version);
        }

        public DocumentType createDocumentType(JSString qualifiedName,
                                               JSString publicId,
                                               JSString systemId) {
            return callFunction<DocumentType>("createDocumentType", qualifiedName, publicId, systemId);
        }

        public Document createDocument(JSString namespaceURI,
                                       JSString qualifiedName,
                                       DocumentType doctype) {
            return callFunction<Document>("createDocument", namespaceURI, qualifiedName, doctype);
        }

        public object getFeature(JSString feature,
                                 JSString version) {
            return callFunction<object>("getFeature", feature, version);
        }
    }

    public class DocumentFragment: Node {
    }

    public class Document: Node, DocumentEvent {

        protected Document() {
        }

        public Document(JSVar src): base(src) {
        }

        public DocumentType doctype {
            get {
                return getProperty<DocumentType>("doctype");
            }
        }

        public DOMImplementation implementation {
            get {
                return getProperty<DOMImplementation>("implementation");
            }
        }

        public Element documentElement {
            get {
                return getProperty<Element>("documentElement");
            }
        }

        public JSString inputEncoding {
            get {
                return getProperty<JSString>("inputEncoding");
            }
        }

        public JSString xmlEncoding {
            get {
                return getProperty<JSString>("xmlEncoding");
            }
        }

        public JSBoolean xmlStandalone {
            get {
                return getProperty<JSBoolean>("xmlStandalone");
            }

            set {
                setProperty("xmlStandalone", value);
            }
        }

        public JSString xmlVersion {
            get {
                return getProperty<JSString>("xmlVersion");
            }

            set {
                setProperty("xmlVersion", value);
            }
        }

        public JSBoolean strictErrorChecking {
            get {
                return getProperty<JSBoolean>("strictErrorChecking");
            }

            set {
                setProperty("strictErrorChecking", value);
            }
        }

        public JSString documentURI {
            get {
                return getProperty<JSString>("documentURI");
            }

            set {
                setProperty("documentURI", value);
            }
        }

        public DOMConfiguration domConfig {
            get {
                return getProperty<DOMConfiguration>("domConfig");
            }
        }

        public Element createElement(JSString tagName) {
            return callFunction<Element>("createElement", tagName);
        }

        public DocumentFragment createDocumentFragment() {
            return callFunction<DocumentFragment>("createDocumentFragment");
        }

        public Text createTextNode(JSString data) {
            return callFunction<Text>("createTextNode", data);
        }

        public Comment createComment(JSString data) {
            return callFunction<Comment>("createComment", data);
        }

        public CDATASection createCDATASection(JSString data) {
            return callFunction<CDATASection>("createCDATASection", data);
        }

        public ProcessingInstruction createProcessingInstruction(JSString target, JSString data) {
            return callFunction<ProcessingInstruction>("ProcessingInstruction", target, data);
        }

        public Attr createAttribute(JSString name) {
            return callFunction<Attr>("createAttribute", name);
        }

        public EntityReference createEntityReference(JSString name) {
            return callFunction<EntityReference>("createEntityReference", name);
        }

        public NodeList getElementsByTagName(JSString tagname) {
            return callFunction<NodeList>("getElementsByTagName", tagname);
        }

        public Node importNode(Node importedNode,
                               JSBoolean deep) {

            return callFunction<Node>("importNode", importedNode, deep);
        }

        public Element createElementNS(JSString namespaceURI,
                                       JSString qualifiedName) {
            return callFunction<Element>("createElementNS", namespaceURI, qualifiedName);
        }

        public Attr createAttributeNS(JSString namespaceURI,
                                      JSString qualifiedName) {
            return callFunction<Attr>("createAttributeNS", namespaceURI, qualifiedName);
        }

        public NodeList getElementsByTagNameNS(JSString namespaceURI,
                                               JSString localName) {
            return callFunction<NodeList>("getElementsByTagNameNS", namespaceURI, localName);
        }

        /// <summary>
        /// This is a generic method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="elementId"></param>
        /// <returns></returns>
        public T getElementById<T>(JSString elementId) where T: Element {
            JSVar elm = new JSVar(callFunction<JSVar>("getElementById", elementId));
            return createVariable<T>(elm);
        }

        public Node adoptNode(Node source) {
            return callFunction<Node>("adoptNode", source);
        }

        public void normalizeDocument() {
            callProcedure("normalizeDocument");
        }

        public Node renameNode(Node n,
                               JSString namespaceURI,
                               JSString qualifiedName) {
            return callFunction<Node>("renameNode", n, namespaceURI, qualifiedName);
        }

        public Event<T> createEvent<T>(JSString eventType) where T: EventTarget {
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            return JSStmt.TERNARY<Event<T>>(isIE, callFunctionInline<JSVar>("createEventObject"), callFunctionInline<JSVar>("createEvent", eventType));
        }

        public JSBoolean canDispatch(JSString namespaceURI,
                               JSString type) {
            return callFunction<JSBoolean>("canDispatch", namespaceURI, type);
        }
    }

    public class Node: JSVar, EventTarget {

        public const short ELEMENT_NODE = 1;
        public const short ATTRIBUTE_NODE = 2;
        public const short TEXT_NODE = 3;
        public const short CDATA_SECTION_NODE = 4;
        public const short ENTITY_REFERENCE_NODE = 5;
        public const short ENTITY_NODE = 6;
        public const short PROCESSING_INSTRUCTION_NODE = 7;
        public const short COMMENT_NODE = 8;
        public const short DOCUMENT_NODE = 9;
        public const short DOCUMENT_TYPE_NODE = 10;
        public const short DOCUMENT_FRAGMENT_NODE = 11;
        public const short NOTATION_NODE = 12;

        public const short DOCUMENT_POSITION_DISCONNECTED = 0x01;
        public const short DOCUMENT_POSITION_PRECEDING = 0x02;
        public const short DOCUMENT_POSITION_FOLLOWING = 0x04;
        public const short DOCUMENT_POSITION_CONTAINS = 0x08;
        public const short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
        public const short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;

        protected Node() {
        }

        public Node(JSVar src)
            : base(src) {
        }

        public JSString nodeName {
            get {
                return getProperty<JSString>("nodeName");
            }
        }

        public JSString nodeValue {
            get {
                return getProperty<JSString>("nodeValue");
            }
            set {
                setProperty("nodeValue", value);
            }
        }

        public JSNumber nodeType {
            get {
                return getProperty<JSNumber>("nodeType");
            }
            set {
                setProperty("nodeType", value);
            }
        }

        public Node parentNode {
            get {
                return getProperty<Node>("parentNode");
            }
        }

        public Node firstChild {
            get {
                return getProperty<Node>("firstChild");
            }
        }

        public Node lastChild {
            get {
                return getProperty<Node>("lastChild");
            }
        }

        public NodeList childNodes {
            get {
                return getProperty<NodeList>("childNodes");
            }
        }

        public Node previousSibling {
            get {
                return getProperty<Node>("previousSibling");
            }
        }

        public Node nextSibling {
            get {
                return getProperty<Node>("nextSibling");
            }
        }

        public NamedNodeMap attributes {
            get {
                return getProperty<NamedNodeMap>("attributes");
            }
        }

        public Document ownerDocument {
            get {
                return getProperty<Document>("ownerDocument");
            }
        }

        public JSString namespaceURI {
            get {
                return getProperty<JSString>("namespaceURI");
            }
        }

        public JSString prefix {
            get {
                return getProperty<JSString>("prefix");
            }

            set {
                setProperty("prefix", value);
            }
        }

        public JSString localName {
            get {
                return getProperty<JSString>("localName");
            }
        }

        public JSString baseURI {
            get {
                return getProperty<JSString>("baseURI");
            }
        }

        public JSString textContent {
            get {
                JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
                return JSStmt.TERNARY<JSString>(isIE, getPropertyInline<JSVar>("innerText"), getPropertyInline<JSVar>("textContent"));
            }

            set {

                // create a reference
                JSVar v = new JSVar(value.ToString());

                JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
                JSStmt.IF_ELSE(isIE, () => {
                    setProperty("innerText", v);
                }, () => {
                    setProperty("textContent", v);
                });
            }
        }

        public Node insertBefore(Node newChild, Node refChild) {
            return callFunction<Node>("insertBefore", newChild, refChild);
        }

        public Node replaceChild(Node newChild, Node oldChild) {
            return callFunction<Node>("replaceChild", newChild, oldChild);
        }

        public Node removeChild(Node oldChild) {
            return callFunction<Node>("removeChild", oldChild);
        }
        public Node appendChild(Node newChild) {
            return callFunction<Node>("appendChild", newChild);
        }

        public JSBoolean hasChildNodes() {
            return callFunction<JSBoolean>("hasChildNodes");
        }

        public Node cloneNode(JSBoolean deep) {
            return callFunction<Node>("cloneNode", deep);
        }

        public void normalize() {
            callProcedure("normalize");
        }

        public JSBoolean isSupported(JSString feature, JSString version) {
            return callFunction<JSBoolean>("isSupported", feature, version);
        }

        public JSBoolean hasAttributes() {
            return callFunction<JSBoolean>("hasAttributes");
        }

        public JSNumber compareDocumentPosition(Node other) {
            return callFunction<JSNumber>("compareDocumentPosition", other);
        }

        public JSBoolean isSameNode(Node other) {
            return callFunction<JSBoolean>("isSameNode", other);
        }

        public JSBoolean lookupPrefix(JSString namespaceURI) {
            return callFunction<JSBoolean>("lookupPrefix", namespaceURI);
        }

        public JSBoolean isDefaultNamespace(JSString namespaceURI) {
            return callFunction<JSBoolean>("isDefaultNamespace", namespaceURI);
        }

        public JSString lookupNamespaceURI(JSString prefix) {
            return callFunction<JSString>("lookupNamespaceURI", prefix);
        }

        public JSBoolean isEqualNode(Node arg) {
            return callFunction<JSBoolean>("isEqualNode", arg);
        }

        public JSObject getFeature(JSString feature, JSString version) {
            return callFunction<JSObject>("getFeature", feature, version);
        }

        public JSVar setUserData(JSString key, JSVar data, UserDataHandler handler) {
            return callFunction<JSVar>("setUserData", key, data, handler);
        }

        public JSVar getUserData(JSString key) {
            return callFunction<JSVar>("getUserData", key);
        }

        /// <summary>
        /// Adds a general event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void addEventListener<T>(string type,
                                     EventListener<T> listener,
                                     bool useCapture) where T: EventTarget {
            Event<T> e = new Event<T>();
            string name = listener.Target.ToString() + '_' + listener.Method.Name;

            JSFunction f = new JSFunction(name, listener, new object[] { e });

            HtmlWindow.setVariable(name, f);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("attachEvent", new object[] { "'on" + type + "'", f });
            }, () => {
                callProcedure("addEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Adds a mouse event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void addEventListener<T>(string type,
                                     MouseEventListener<T> listener,
                                     bool useCapture) where T: EventTarget {
            MouseEvent<T> e = new MouseEvent<T>();
            string name = listener.Target.ToString() + '_' + listener.Method.Name;

            JSFunction f = new JSFunction(name, listener, new object[] { e });

            HtmlWindow.setVariable(name, f);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("attachEvent", new object[] { "'on" + type + "'", f });
            }, () => {
                callProcedure("addEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Adds a keyboard event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void addEventListener<T>(string type,
                                     KeyboardEventListener<T> listener,
                                     bool useCapture) where T: EventTarget {
            KeyboardEvent<T> e = new KeyboardEvent<T>();
            string name = listener.Target.ToString() + '_' + listener.Method.Name;

            JSFunction f = new JSFunction(name, listener, new object[] { e });

            HtmlWindow.setVariable(name, f);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("attachEvent", new object[] { "'on" + type + "'", f });
            }, () => {
                callProcedure("addEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Adds a custom event listener.
        /// </summary>
        /// <typeparam name="T">Any EventTarget</typeparam>
        /// <param name="type">name of event</param>
        /// <param name="listener">event listener</param>
        /// <param name="useCapture">useful only for non-IE</param>
        public void addCustomEventListener<T>(string type,
                                     EventListener<T> listener,
                                     bool useCapture) where T: EventTarget {

            Event<T> e = new Event<T>();
            string name = listener.Target.ToString() + '_' + listener.Method.Name;

            // delegate that wraps detection of matching event name
            EventListener<T> a = delegate(Event<T> evt) {
                JSString evName = e.getProperty<JSString>("eventName");
                JSStmt.IF(JSStmt.AND(evName, evName != type), () => {
                    JSStmt.RETURN();
                });

                listener.DynamicInvoke(evt);
            };

            JSFunction f = new JSFunction(name, a, new object[] { e });

            HtmlWindow.setVariable(name, f);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("attachEvent", new object[] { "'ondataavailable'", f });
            }, () => {
                callProcedure("addEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Removes a general event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void removeEventListener<T>(string type,
                                        EventListener<T> listener,
                                        bool useCapture) where T: EventTarget {
            string name = listener.Target.ToString() + '_' + listener.Method.Name;
            JSVar f = HtmlWindow.getVariable<JSVar>(name);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("detachEvent", new object[] { "'on" + type + "'", f });
            }, () => {
                callProcedure("removeEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Removes a mouse event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void removeEventListener<T>(string type,
                                        MouseEventListener<T> listener,
                                        bool useCapture) where T: EventTarget {
            string name = listener.Target.ToString() + '_' + listener.Method.Name;
            JSVar f = HtmlWindow.getVariable<JSVar>(name);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("detachEvent", new object[] { "'on" + type + "'", f });
            }, () => {
                callProcedure("removeEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Removes a keyboard event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void removeEventListener<T>(string type,
                                        KeyboardEventListener<T> listener,
                                        bool useCapture) where T: EventTarget {
            string name = listener.Target.ToString() + '_' + listener.Method.Name;
            JSVar f = HtmlWindow.getVariable<JSVar>(name);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("detachEvent", new object[] { "'on" + type + "'", f });
            }, () => {
                callProcedure("removeEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        /// <summary>
        /// Removes a custom event listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="listener"></param>
        /// <param name="useCapture"></param>
        public void removeCustomEventListener<T>(string type,
                                        EventListener<T> listener,
                                        bool useCapture) where T: EventTarget {
            string name = listener.Target.ToString() + '_' + listener.Method.Name;
            JSVar f = HtmlWindow.getVariable<JSVar>(name);
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSStmt.IF_ELSE(isIE, () => {
                callProcedure("detachEvent", new object[] { "'ondatavailable'", f });
            }, () => {
                callProcedure("removeEventListener", new object[] { "'" + type + "'", f, useCapture });
            });
        }

        public JSBoolean dispatchEvent<T>(Event<T> evt) where T: EventTarget {
            return callFunction<JSBoolean>("dispatchEvent", evt);
        }

        /// <summary>
        /// Fires an event. Not part of the standard, but a combination
        /// of IE and Firefox event model.
        /// 
        /// The eventTypeArg is any event accepted by standard browser, such
        /// as "click", "mousedown". Notice that they do not have prefix "on".
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="eventTypeArg">any well known event</param>
        /// <param name="canBubbleArg"></param>
        /// <param name="cancelableArg"></param>
        /// <param name="evt"></param>
        /// <returns></returns>
        public JSBoolean fireEvent<T>(JSString eventTypeArg,
                              JSBoolean canBubbleArg,
                              JSBoolean cancelableArg,
                              Event<T> evt) where T: EventTarget {
            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSVar ret = new JSVar("null");
            JSStmt.IF_ELSE(isIE, () => {
                evt.setProperty("type", eventTypeArg);
                ret.set(callFunction<JSBoolean>("fireEvent", "'on" + eventTypeArg.ToString().Substring(1), evt));
            }, () => {
                evt.initEvent(eventTypeArg, canBubbleArg, cancelableArg);
                ret.set(callFunction<JSBoolean>("dispatchEvent", evt));
            });

            return JSVar.createVariable<JSBoolean>(ret);
        }

        /// <summary>
        /// Fires a custom event with custom data.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="eventTypeArg"></param>
        /// <param name="canBubbleArg"></param>
        /// <param name="cancelableArg"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public JSBoolean fireCustomEvent<T>(JSString eventTypeArg,
                              JSBoolean canBubbleArg,
                              JSBoolean cancelableArg,
                              JSObject data) where T: EventTarget {

            Document doc = JSVar.getExistingVariable<Document>("document");
            Event<T> evt = doc.createEvent<T>("Event");
            if (data != null) {
                evt.setProperty("data", data);
            }
            evt.setProperty("eventName", eventTypeArg);

            JSBoolean isIE = JSVar.getExistingVariable<JSBoolean>("monadsphere.isIE");
            JSVar ret = new JSVar("null");
            JSStmt.IF_ELSE(isIE, () => {
                evt.setProperty("eventType", "'ondataavailable'");
                ret.set(callFunction<JSBoolean>("fireEvent", "'ondataavailable'", evt));
            }, () => {
                evt.initEvent(eventTypeArg, canBubbleArg, cancelableArg);
                ret.set(callFunction<JSBoolean>("dispatchEvent", evt));
            });

            return JSVar.createVariable<JSBoolean>(ret);
        }
    }

    public class NodeList: JSVar {

        protected NodeList() {
        }

        public NodeList(JSVar src)
            : base(src) {
        }

        public JSNumber length {
            get {
                return getProperty<JSNumber>("length");
            }
        }

        public Node item(JSNumber index) {
            return callFunction<Node>("item", index);
        }

        public Node this[JSNumber index] {
            get {
                return getIndex<Node>(index);
            }
        }
    }

    public class NamedNodeMap: JSVar {
        public NamedNodeMap() {
        }

        public JSNumber length {
            get {
                return getProperty<JSNumber>("length");
            }
        }

        public Node getNamedItem(JSString name) {
            return callFunction<Node>("getNamedItem", name);
        }

        public Node setNamedItem(Node arg) {
            return callFunction<Node>("setNamedItem", arg);
        }

        public Node removeNamedItem(JSString name) {
            return callFunction<Node>("removeNamedItem", name);
        }

        public Node item(JSNumber index) {
            return callFunction<Node>("item", index);
        }

        public Node this[JSNumber index] {
            get {
                return getIndex<Node>(index);
            }
        }

        public Node getNamedItemNS(JSString namespaceURI, JSString localName) {
            return callFunction<Node>("getNamedItemNS", namespaceURI, localName);
        }

        public Node setNamedItemNS(Node arg) {
            return callFunction<Node>("setNamedItemNS", arg);
        }

        public Node removeNamedItemNS(JSString namespaceURI,
                                           JSString localName) {
            return callFunction<Node>("setNamedItemNS", namespaceURI, localName);
        }
    }

    public class CharacterData: Node {

        public CharacterData(JSVar src)
            : base(src) {
        }

        public JSString data {
            get {
                return getProperty<JSString>("data");
            }
            set {
                setProperty("data", value);
            }
        }

        public JSNumber length {
            get {
                return getProperty<JSNumber>("length");
            }
        }

        public JSString substringData(JSNumber offset,
                                    JSNumber count) {
            return callFunction<JSString>("substringData", offset, count);
        }

        public void appendData(JSString arg) {
            callProcedure("appendData", arg);
        }

        public void insertData(JSNumber offset,
                               JSString arg) {
            callProcedure("insertData", offset, arg);
        }

        public void deleteData(JSNumber offset,
                               JSNumber count) {
            callProcedure("deleteData", offset, count);
        }

        public void replaceData(JSNumber offset,
                                JSNumber count,
                                JSString arg) {
            callProcedure("replaceData", offset, count, arg);
        }
    }

    public class Attr: Node {
        protected Attr() {
        }

        public Attr(JSVar src)
            : base(src) {
        }

        public JSString name {
            get {
                return getProperty<JSString>("name");
            }
        }

        public JSBoolean specified {
            get {
                return getProperty<JSBoolean>("specified");
            }
        }

        public JSString value {
            get {
                return getProperty<JSString>("value");
            }

            set {
                setProperty("value", value);
            }
        }

        public Element ownerElement {
            get {
                return getProperty<Element>("ownerElement");
            }
        }

        public TypeInfo schemaTypeInfo {
            get {
                return getProperty<TypeInfo>("schemaTypeInfo");
            }
        }

        public JSBoolean isId {
            get {
                return getProperty<JSBoolean>("isId");
            }
        }
    }

    public class Element: Node {
        protected Element() {
        }

        public Element(JSVar src)
            : base(src) {
        }

        public JSString tagName {
            get {
                return getProperty<JSString>("tagName");
            }
        }

        public TypeInfo schemaTypeInfo {
            get {
                return getProperty<TypeInfo>("schemaTypeInfo");
            }
        }

        public JSString getAttribute(JSString name) {
            return callFunction<JSString>("getAttribute", name);
        }

        public void setAttribute(JSString name, JSString value) {
            callProcedure("setAttribute", name, value);
        }

        public void removeAttribute(JSString name) {
            callProcedure("removeAttribute", name);
        }

        public Attr getAttributeNode(JSString name) {
            return callFunction<Attr>("getAttributeNode", name);
        }

        public Attr setAttributeNode(Attr newAttr) {
            return callFunction<Attr>("setAttributeNode", newAttr);
        }

        public Attr removeAttributeNode(Attr oldAttr) {
            return callFunction<Attr>("removeAttributeNode", oldAttr);
        }

        public NodeList getElementsByTagName(JSString name) {
            return callFunction<NodeList>("getElementsByTagName", name);
        }

        public JSString getAttributeNS(JSString namespaceURI, JSString localName) {
            return callFunction<JSString>("getAttributeNS", namespaceURI, localName);
        }

        public void setAttributeNS(JSString namespaceURI, JSString qualifiedName, JSString value) {
            callProcedure("setAttributeNS", namespaceURI, qualifiedName, value);
        }

        public void removeAttributeNS(JSString namespaceURI, JSString localName) {
            callProcedure("removeAttributeNS", namespaceURI, localName);
        }

        public Attr getAttributeNodeNS(JSString namespaceURI, JSString localName) {
            return callFunction<Attr>("getAttributeNodeNS", namespaceURI, localName);
        }

        public Attr setAttributeNodeNS(Attr newAttr) {
            return callFunction<Attr>("setAttributeNodeNS", newAttr);
        }

        public NodeList getElementsByTagNameNS(JSString namespaceURI, JSString localName) {
            return callFunction<NodeList>("getElementsByTagNameNS", namespaceURI, localName);
        }

        public JSBoolean hasAttribute(JSString name) {
            return callFunction<JSBoolean>("hasAttribute", name);
        }

        public JSBoolean hasAttributeNS(JSString namespaceURI, JSString localName) {
            return callFunction<JSBoolean>("hasAttributeNS", namespaceURI, localName);
        }

        public void setIdAttribute(JSString name, JSBoolean isId) {
            callProcedure("hasAttributeNS", name, isId);
        }

        public void setIdAttributeNS(JSString namespaceURI, JSString localName, JSBoolean isId) {
            callProcedure("setIdAttributeNS", namespaceURI, localName, isId);
        }

        public void setIdAttributeNode(Attr idAttr, JSBoolean isId) {
            callProcedure("setIdAttributeNode", idAttr, isId);
        }
    }

    public class Text: CharacterData {

        public Text(JSVar src)
            : base(src) {
        }

        public JSBoolean isElementContentWhitespace {
            get {
                return getProperty<JSBoolean>("isElementContentWhitespace");
            }
        }

        public JSString wholeText {
            get {
                return getProperty<JSString>("wholeText");
            }
        }

        public Text splitText(JSNumber offset) {
            return callFunction<Text>("splitText", offset);
        }


        public Text replaceWholeText(JSString content) {
            return callFunction<Text>("replaceWholeText", content);
        }
    }

    public class Comment: CharacterData {
        public Comment(JSVar src)
            : base(src) {
        }
    }

    public class TypeInfo: JSVar {

        public const int DERIVATION_RESTRICTION = 0x00000001;
        public const int DERIVATION_EXTENSION = 0x00000002;
        public const int DERIVATION_UNION = 0x00000004;
        public const int DERIVATION_LIST = 0x00000008;

        public TypeInfo() {
        }

        public JSString typeName {

            get {
                return getProperty<JSString>("typeName");
            }
        }

        public JSString typeNamespace {
            get {
                return getProperty<JSString>("typeNamespace");
            }
        }

        public JSBoolean isDerivedFrom(JSString typeNamespaceArg,
                                     JSString typeNameArg,
                                     JSNumber derivationMethod) {
            return callFunction<JSBoolean>("isDerivedFrom", typeNamespaceArg, typeNameArg, derivationMethod);
        }
    }

    public abstract class UserDataHandler: JSVar {

        public const short NODE_CLONED = 1;
        public const short NODE_IMPORTED = 2;
        public const short NODE_DELETED = 3;
        public const short NODE_RENAMED = 4;
        public const short NODE_ADOPTED = 5;

        public abstract void handle(JSNumber operation,
                           JSString key,
                           JSVar data,
                           Node src,
                           Node dst);
    }

    public class DOMLocator: JSVar {
        public JSNumber lineNumber {
            get {
                return getProperty<JSNumber>("lineNumber");
            }
        }

        public JSNumber columnNumber {
            get {
                return getProperty<JSNumber>("columnNumber");
            }
        }

        public JSNumber byteOffset {
            get {
                return getProperty<JSNumber>("byteOffset");
            }
        }

        public JSNumber utf16Offset {
            get {
                return getProperty<JSNumber>("utf16Offset");
            }
        }

        public Node relatedNode {
            get {
                return getProperty<Node>("relatedNode");
            }
        }

        public JSString uri {
            get {
                return getProperty<JSString>("uri");
            }
        }
    }

    public class DOMConfiguration: JSVar {
        public DOMStringList parameterNames {
            get {
                return getProperty<DOMStringList>("parameterNames");
            }
        }

        public void setParameter(JSString name,
                             JSVar value) {
            callProcedure("setParameter", name, value);
        }

        public JSVar getParameter(JSString name) {
            return callFunction<JSVar>("getParameter", name);
        }

        public JSBoolean canSetParameter(JSString name,
                                       JSVar value) {
            return callFunction<JSBoolean>("canSetParameter", name, value);
        }
    }

    public class CDATASection: Text {
        public CDATASection(JSVar src)
            : base(src) {
        }
    }

    public class DocumentType: Node {
        public JSString name {
            get {
                return getProperty<JSString>("name");
            }
        }

        public NamedNodeMap entities {
            get {
                return getProperty<NamedNodeMap>("entities");
            }
        }

        public NamedNodeMap notations {
            get {
                return getProperty<NamedNodeMap>("notations");
            }
        }

        public JSString publicId {
            get {
                return getProperty<JSString>("publicId");
            }
        }

        public JSString systemId {
            get {
                return getProperty<JSString>("systemId");
            }
        }

        public JSString internalSubset {
            get {
                return getProperty<JSString>("internalSubset");
            }
        }
    }

    public class Notation: Node {
        public JSString publicId {
            get {
                return getProperty<JSString>("publicId");
            }
        }

        public JSString systemId {
            get {
                return getProperty<JSString>("systemId");
            }
        }
    }

    public class Entity: Node {
        public JSString publicId {
            get {
                return getProperty<JSString>("publicId");
            }
        }

        public JSString systemId {
            get {
                return getProperty<JSString>("systemId");
            }
        }

        public JSString notationName {
            get {
                return getProperty<JSString>("notationName");
            }
        }

        public JSString inputEncoding {
            get {
                return getProperty<JSString>("inputEncoding");
            }
        }

        public JSString xmlEncoding {
            get {
                return getProperty<JSString>("xmlEncoding");
            }
        }

        public JSString xmlVersion {
            get {
                return getProperty<JSString>("xmlVersion");
            }
        }
    }

    public class EntityReference: Node {
    }

    public class ProcessingInstruction: Node {
        public JSString target {
            get {
                return getProperty<JSString>("target");
            }
        }

        public JSString data {
            get {
                return getProperty<JSString>("data");
            }
            set {
                setProperty("data", value);
            }
        }
    }
}
